home *** CD-ROM | disk | FTP | other *** search
/ Maclife 157 / MACLIFE157-2001-09.ISO.7z / MACLIFE157-2001-09.ISO / Linux / MacOS Tools / Other / BootX 1.1.3 (for Old Mac OS) / Sources / lib / From MoreFiles / Search.c < prev    next >
Text File  |  2001-07-23  |  37KB  |  1,276 lines

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    IndexedSearch and the PBCatSearch compatibility function.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        Search.c
  9. **
  10. **    Copyright ゥ 1992-1998 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Gestalt.h>
  24. #include <Timer.h>
  25. #include <Errors.h>
  26. #include <Memory.h>
  27. #include <Files.h>
  28. #include <TextUtils.h>
  29.  
  30. #define    __COMPILINGMOREFILES
  31.  
  32. #include "MoreFiles.h"
  33. #include "MoreFilesExtras.h"
  34. #include "Search.h"
  35.  
  36. /*****************************************************************************/
  37.  
  38. enum
  39. {
  40.     /* Number of LevelRecs to add each time the searchStack is grown */
  41.     /* 20 levels is probably more than reasonable for most volumes. */
  42.     /* If more are needed, they are allocated 20 levels at a time. */
  43.     kAdditionalLevelRecs = 20
  44. };
  45.  
  46. /*****************************************************************************/
  47.  
  48. /*
  49. **    LevelRecs are used to store the directory ID and index whenever
  50. **    IndexedSearch needs to either scan a sub-directory, or return control
  51. **    to the caller because the call has timed out or the number of
  52. **    matches requested has been found. LevelRecs are stored in an array
  53. **    used as a stack.
  54. */
  55. struct    LevelRec
  56. {
  57.     long    dirModDate;    /* for detecting most (but not all) catalog changes */
  58.     long    dirID;
  59.     short    index;
  60. };
  61. typedef struct LevelRec LevelRec;
  62. typedef LevelRec *LevelRecPtr, **LevelRecHandle;
  63.  
  64.  
  65. /*
  66. **    SearchPositionRec is my version of a CatPositionRec. It holds the
  67. **    information I need to resuming searching.
  68. */
  69. #if PRAGMA_ALIGN_SUPPORTED
  70. #pragma options align=mac68k
  71. #endif
  72. struct SearchPositionRec
  73. {
  74.     long            initialize;        /* Goofy checksum of volume information used to make */
  75.                                     /* sure we're resuming a search on the same volume. */
  76.     unsigned short    stackDepth;        /* Current depth on searchStack. */
  77.     short            priv[11];        /* For future use... */
  78. };
  79. #if PRAGMA_ALIGN_SUPPORTED
  80. #pragma options align=reset
  81. #endif
  82. typedef struct SearchPositionRec SearchPositionRec;
  83. typedef SearchPositionRec *SearchPositionRecPtr;
  84.  
  85.  
  86. /*
  87. **    ExtendedTMTask is a TMTask record extended to hold the timer flag.
  88. */
  89. #if PRAGMA_ALIGN_SUPPORTED
  90. #pragma options align=mac68k
  91. #endif
  92. struct ExtendedTMTask
  93. {
  94.     TMTask            theTask;
  95.     Boolean            stopSearch;        /* the Time Mgr task will set stopSearch to */
  96.                                     /* true when the timer expires */
  97. };
  98. #if PRAGMA_ALIGN_SUPPORTED
  99. #pragma options align=reset
  100. #endif
  101. typedef struct ExtendedTMTask ExtendedTMTask;
  102. typedef ExtendedTMTask *ExtendedTMTaskPtr;
  103.  
  104. /*****************************************************************************/
  105.  
  106. static    OSErr    CheckVol(ConstStr255Param pathname,
  107.                          short vRefNum,
  108.                          short *realVRefNum,
  109.                          long *volID);
  110.  
  111. static    OSErr    CheckStack(unsigned short stackDepth,
  112.                            LevelRecHandle searchStack,
  113.                            Size *searchStackSize);
  114.  
  115. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  116.                              Boolean *includeFiles,
  117.                              Boolean *includeDirs,
  118.                              Boolean *includeNames);
  119.  
  120. static    Boolean    IsSubString(ConstStr255Param aStringPtr,
  121.                             ConstStr255Param subStringPtr);
  122.  
  123. static    Boolean    CompareMasked(const long *data1,
  124.                               const long *data2,
  125.                               const long *mask,
  126.                               short longsToCompare);
  127.  
  128. static    void    CheckForMatches(CInfoPBPtr cPB,
  129.                                 CSParamPtr userPB,
  130.                                 const Str63 matchName,
  131.                                 Boolean includeFiles,
  132.                                 Boolean includeDirs);
  133.  
  134. #if    __WANTPASCALELIMINATION
  135. #undef    pascal
  136. #endif
  137.  
  138. #if GENERATINGCFM
  139.  
  140. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr);
  141.  
  142. #else
  143.  
  144. static    pascal    TMTaskPtr    GetTMTaskPtr(void);
  145.  
  146. static    void    TimeOutTask(void);
  147.  
  148. #endif
  149.  
  150. #if    __WANTPASCALELIMINATION
  151. #define    pascal    
  152. #endif
  153.  
  154. static    long    GetDirModDate(short vRefNum,
  155.                               long dirID);
  156.  
  157. /*****************************************************************************/
  158.  
  159. /*
  160. **    CheckVol gets the volume's real vRefNum and builds a volID. The volID
  161. **    is used to help insure that calls to resume searching with IndexedSearch
  162. **    are to the same volume as the last call to IndexedSearch.
  163. */
  164. static    OSErr    CheckVol(ConstStr255Param pathname,
  165.                          short vRefNum,
  166.                          short *realVRefNum,
  167.                          long *volID)
  168. {
  169.     HParamBlockRec pb;
  170.     OSErr error;
  171.  
  172.     error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
  173.     if ( error == noErr )
  174.     {
  175.         /* Return the real vRefNum */
  176.         *realVRefNum = pb.volumeParam.ioVRefNum;
  177.  
  178.         /* Add together a bunch of things that aren't supposed to change on */
  179.         /* a mounted volume that's being searched and that should come up with */
  180.         /* a fairly unique number */
  181.         *volID = pb.volumeParam.ioVCrDate +
  182.                  pb.volumeParam.ioVRefNum +
  183.                  pb.volumeParam.ioVNmAlBlks +
  184.                  pb.volumeParam.ioVAlBlkSiz +
  185.                  pb.volumeParam.ioVFSID;
  186.     }
  187.     return ( error );
  188. }
  189.  
  190. /*****************************************************************************/
  191.  
  192. /*
  193. **    CheckStack checks the size of the search stack (array) to see if there's
  194. **    room to push another LevelRec. If not, CheckStack grows the stack by
  195. **    another kAdditionalLevelRecs elements.
  196. */
  197. static    OSErr    CheckStack(unsigned short stackDepth,
  198.                            LevelRecHandle searchStack,
  199.                            Size *searchStackSize)
  200. {
  201.     OSErr    result;
  202.     
  203.     if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
  204.     {
  205.         /* Time to grow stack */
  206.         SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
  207.         result = MemError();    /* should be noErr */
  208.         *searchStackSize = InlineGetHandleSize((Handle)searchStack);
  209.     }
  210.     else
  211.     {
  212.         result = noErr;
  213.     }
  214.     
  215.     return ( result );
  216. }
  217.  
  218. /*****************************************************************************/
  219.  
  220. /*
  221. **    VerifyUserPB makes sure the parameter block passed to IndexedSearch has
  222. **    valid parameters. By making this check once, we don't have to worry about
  223. **    things like NULL pointers, strings being too long, etc.
  224. **    VerifyUserPB also determines if the search includes files and/or
  225. **    directories, and determines if a full or partial name search was requested.
  226. */
  227. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  228.                              Boolean *includeFiles,
  229.                              Boolean *includeDirs,
  230.                              Boolean *includeNames)
  231. {
  232.     CInfoPBPtr    searchInfo1;
  233.     CInfoPBPtr    searchInfo2;
  234.     
  235.     searchInfo1 = userPB->ioSearchInfo1;
  236.     searchInfo2 = userPB->ioSearchInfo2;
  237.     
  238.     /* ioMatchPtr cannot be NULL */
  239.     if ( userPB->ioMatchPtr == NULL )
  240.     {
  241.         goto ParamErrExit;
  242.     }
  243.     
  244.     /* ioSearchInfo1 cannot be NULL */
  245.     if ( searchInfo1 == NULL )
  246.     {
  247.         goto ParamErrExit;
  248.     }
  249.     
  250.     /* If any bits except partialName, fullName, or negate are set, then */
  251.     /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
  252.     if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
  253.          ( searchInfo2 == NULL ))
  254.     {
  255.         goto ParamErrExit;
  256.     }
  257.     
  258.     *includeFiles = false;
  259.     *includeDirs = false;
  260.     *includeNames = false;
  261.     
  262.     if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
  263.     {
  264.         /* If any kind of name matching is requested, then ioNamePtr in */
  265.         /* ioSearchInfo1 cannot be NULL or a zero-length string */
  266.         if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
  267.              (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
  268.              (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
  269.         {
  270.             goto ParamErrExit;
  271.         }
  272.         
  273.         *includeNames = true;
  274.     }
  275.     
  276.     if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
  277.     {
  278.         /* The only attributes you can search on are the directory flag */
  279.         /* and the locked flag. */
  280.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
  281.         {
  282.             goto ParamErrExit;
  283.         }
  284.         
  285.         /* interested in the directory bit? */
  286.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  287.         {
  288.             /* yes, so do they want just directories or just files? */
  289.             if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  290.             {
  291.                 *includeDirs = true;
  292.             }
  293.             else
  294.             {
  295.                 *includeFiles = true;
  296.             }
  297.         }
  298.         else
  299.         {
  300.             /* no interest in directory bit - get both files and directories */
  301.             *includeDirs = true;
  302.             *includeFiles = true;
  303.         }
  304.     }
  305.     else
  306.     {
  307.         /* no attribute checking - get both files and directories */
  308.         *includeDirs = true;
  309.         *includeFiles = true;
  310.     }
  311.     
  312.     /* If directories are included in the search, */
  313.     /* then the locked attribute cannot be requested. */
  314.     if ( *includeDirs &&
  315.          ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
  316.          ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
  317.     {
  318.         goto ParamErrExit;
  319.     }
  320.     
  321.     /* If files are included in the search, then there cannot be */
  322.     /* a search on the number of files. */
  323.     if ( *includeFiles &&
  324.          ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
  325.     {
  326.         goto ParamErrExit;
  327.     }
  328.     
  329.     /* If directories are included in the search, then there cannot */
  330.     /* be a search on file lengths. */
  331.     if ( *includeDirs &&
  332.          ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
  333.     {
  334.         goto ParamErrExit;
  335.     }
  336.     
  337.     return ( noErr );
  338.          
  339. ParamErrExit:
  340.     return ( paramErr );
  341. }
  342.  
  343. /*****************************************************************************/
  344.  
  345. /*
  346. **    IsSubString checks to see if a string is a substring of another string.
  347. **    Both input strings have already been converted to all uppercase using
  348. **    UprString (the same non-international call the File Manager uses).
  349. */
  350. static    Boolean    IsSubString(ConstStr255Param aStringPtr,
  351.                             ConstStr255Param subStringPtr)
  352. {
  353.     short    strLength;        /* length of string */
  354.     short    subStrLength;    /* length of subString */
  355.     Boolean    found;            /* result of test */
  356.     short    index;            /* current index into string */
  357.     
  358.     found = false;
  359.     strLength = aStringPtr[0];
  360.     subStrLength = subStringPtr[0];
  361.         
  362.     if ( subStrLength <= strLength)
  363.     {
  364.         register short    count;            /* search counter */
  365.         register short    strIndex;        /* running index into string */
  366.         register short    subStrIndex;    /* running index into subString */
  367.         
  368.         /* start looking at first character */
  369.         index = 1;
  370.         
  371.         /* continue looking until remaining string is shorter than substring */
  372.         count = strLength - subStrLength + 1;
  373.         
  374.         do
  375.         {
  376.             strIndex = index;    /* start string index at index */
  377.             subStrIndex = 1;    /* start subString index at 1 */
  378.             
  379.             while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
  380.             {
  381.                 if ( subStrIndex == subStrLength )
  382.                 {
  383.                     /* all characters in subString were found */
  384.                     found = true;
  385.                 }
  386.                 else
  387.                 {
  388.                     /* check next character of substring against next character of string */
  389.                     ++subStrIndex;
  390.                     ++strIndex;
  391.                 }
  392.             }
  393.             
  394.             if ( !found )
  395.             {
  396.                 /* start substring search again at next string character */
  397.                 ++index;
  398.                 --count;
  399.             }
  400.         } while ( count != 0 && (!found) );
  401.     }
  402.     
  403.     return ( found );
  404. }
  405.  
  406. /*****************************************************************************/
  407.  
  408. /*
  409. **    CompareMasked does a bitwise comparison with mask on 1 or more longs.
  410. **    data1 and data2 are first exclusive-ORed together resulting with bits set
  411. **    where they are different. That value is then ANDed with the mask resulting
  412. **    with bits set if the test fails. true is returned if the tests pass.
  413. */
  414. static    Boolean    CompareMasked(const long *data1,
  415.                               const long *data2,
  416.                               const long *mask,
  417.                               short longsToCompare)
  418. {
  419.     Boolean    result = true;
  420.     
  421.     while ( (longsToCompare != 0) && (result == true) )
  422.     {
  423.         /* (*data1 ^ *data2) = bits that are different, so... */
  424.         /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
  425.         
  426.         if ( ((*data1 ^ *data2) & *mask) != 0 )
  427.             result = false;
  428.         
  429.         ++data1;
  430.         ++data2;
  431.         ++mask;
  432.         --longsToCompare;
  433.     }
  434.     
  435.     return ( result );
  436. }
  437.  
  438. /*****************************************************************************/
  439.  
  440. /*
  441. **    Check for matches compares the search criteria in userPB to the file
  442. **    system object in cPB. If there's a match, then the information in cPB is
  443. **    is added to the match array and the actual match count is incremented.
  444. */
  445. static    void    CheckForMatches(CInfoPBPtr cPB,
  446.                                 CSParamPtr userPB,
  447.                                 const Str63 matchName,
  448.                                 Boolean includeFiles,
  449.                                 Boolean includeDirs)
  450. {
  451.     long        searchBits;
  452.     CInfoPBPtr    searchInfo1;
  453.     CInfoPBPtr    searchInfo2;
  454.     Str63        itemName;        /* copy of object's name for partial name matching */
  455.     Boolean        foundMatch;
  456.     
  457.     foundMatch = false;            /* default to no match */
  458.     
  459.     searchBits = userPB->ioSearchBits;
  460.     searchInfo1 = userPB->ioSearchInfo1;
  461.     searchInfo2 = userPB->ioSearchInfo2;
  462.     
  463.     /* Into the if statements that go on forever... */
  464.     
  465.     if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  466.     {
  467.         if (!includeFiles)
  468.         {
  469.             goto Failed;
  470.         }
  471.     }
  472.     else
  473.     {
  474.         if (!includeDirs)
  475.         {
  476.             goto Failed;
  477.         }
  478.     }
  479.     
  480.     if ( (searchBits & fsSBPartialName) != 0 )
  481.     {
  482.         if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
  483.              (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
  484.         {
  485.             /* Make uppercase copy of object name */
  486.             BlockMoveData(cPB->hFileInfo.ioNamePtr,
  487.                             itemName,
  488.                             cPB->hFileInfo.ioNamePtr[0] + 1);
  489.             /* Use the same non-international call the File Manager uses */
  490.             UpperString(itemName, true);
  491.         }
  492.         else
  493.         {
  494.             goto Failed;
  495.         }
  496.         
  497.         {
  498.             if ( !IsSubString(itemName, matchName) )
  499.             {
  500.                 goto Failed;
  501.             }
  502.             else if ( searchBits == fsSBPartialName )
  503.             {
  504.                 /* optimize for name matching only since it is most common way to search */
  505.                 goto Hit;
  506.             }
  507.         }
  508.     }
  509.     
  510.     if ( (searchBits & fsSBFullName) != 0 )
  511.     {
  512.         /* Use the same non-international call the File Manager uses */
  513.         if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
  514.         {
  515.             goto Failed;
  516.         }
  517.         else if ( searchBits == fsSBFullName )
  518.         {
  519.             /* optimize for name matching only since it is most common way to search */
  520.             goto Hit;
  521.         }
  522.     }
  523.     
  524.     if ( (searchBits & fsSBFlParID) != 0 )
  525.     {
  526.         if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
  527.              ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
  528.         {
  529.             goto Failed;
  530.         }
  531.     }
  532.     
  533.     if ( (searchBits & fsSBFlAttrib) != 0 )
  534.     {
  535.         if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
  536.               searchInfo2->hFileInfo.ioFlAttrib) != 0 )
  537.         {
  538.             goto Failed;
  539.         }
  540.     }
  541.     
  542.     if ( (searchBits & fsSBDrNmFls) != 0 )
  543.     {
  544.         if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
  545.              ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
  546.         {
  547.             goto Failed;
  548.         }
  549.     }
  550.  
  551.     if ( (searchBits & fsSBFlFndrInfo) != 0 )    /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
  552.     {
  553.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
  554.                             (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
  555.                             (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
  556.                             sizeof(FInfo) / sizeof(long)) )
  557.         {
  558.             goto Failed;
  559.         }
  560.     }
  561.     
  562.     if ( (searchBits & fsSBFlXFndrInfo) != 0 )    /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
  563.     {
  564.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
  565.                             (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
  566.                             (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
  567.                             sizeof(FXInfo) / sizeof(long)) )
  568.         {
  569.             goto Failed;
  570.         }
  571.     }
  572.     
  573.     if ( (searchBits & fsSBFlLgLen) != 0 )
  574.     {
  575.         if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
  576.              ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
  577.         {
  578.             goto Failed;
  579.         }
  580.     }
  581.  
  582.     if ( (searchBits & fsSBFlPyLen) != 0 )
  583.     {
  584.         if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
  585.              ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
  586.         {
  587.             goto Failed;
  588.         }
  589.     }
  590.  
  591.     if ( (searchBits & fsSBFlRLgLen) != 0 )
  592.     {
  593.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
  594.              ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
  595.         {
  596.             goto Failed;
  597.         }
  598.     }
  599.  
  600.     if ( (searchBits & fsSBFlRPyLen) != 0 )
  601.     {
  602.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
  603.              ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
  604.         {
  605.             goto Failed;
  606.         }
  607.     }
  608.  
  609.     if ( (searchBits & fsSBFlCrDat) != 0 )    /* fsSBFlCrDat is same as fsSBDrCrDat */
  610.     {
  611.         if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
  612.              ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
  613.         {
  614.             goto Failed;
  615.         }
  616.     }
  617.  
  618.     if ( (searchBits & fsSBFlMdDat) != 0 )    /* fsSBFlMdDat is same as fsSBDrMdDat */
  619.     {
  620.         if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
  621.              ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
  622.         {
  623.             goto Failed;
  624.         }
  625.     }
  626.  
  627.     if ( (searchBits & fsSBFlBkDat) != 0 )    /* fsSBFlBkDat is same as fsSBDrBkDat */
  628.     {
  629.         if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
  630.              ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
  631.         {
  632.             goto Failed;
  633.         }
  634.     }
  635.  
  636.     /* Hey, we passed all of the tests! */
  637.     
  638. Hit:
  639.     foundMatch = true;
  640.  
  641. /* foundMatch is false if code jumps to Failed */
  642. Failed:
  643.     /* Do we reverse our findings? */
  644.     if ( (searchBits & fsSBNegate) != 0 )
  645.     {
  646.         foundMatch = !foundMatch;    /* matches are not, not matches are */
  647.     }
  648.     
  649.     if ( foundMatch )
  650.     {
  651.  
  652.         /* Move the match into the match buffer */
  653.         userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
  654.         userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
  655.         if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
  656.         {
  657.             cPB->hFileInfo.ioNamePtr[0] = 63;
  658.         }
  659.         BlockMoveData(cPB->hFileInfo.ioNamePtr,
  660.                       userPB->ioMatchPtr[userPB->ioActMatchCount].name,
  661.                       cPB->hFileInfo.ioNamePtr[0] + 1);
  662.         
  663.         /* increment the actual count */
  664.         ++(userPB->ioActMatchCount);
  665.     }
  666. }
  667.  
  668. /*****************************************************************************/
  669.  
  670. /*
  671. **    TimeOutTask is executed when the timer goes off. It simply sets the
  672. **    stopSearch field to true. After each object is found and possibly added
  673. **    to the matches buffer, stopSearch is checked to see if the search should
  674. **    continue.
  675. */
  676.  
  677. #if    __WANTPASCALELIMINATION
  678. #undef    pascal
  679. #endif
  680.  
  681. #if GENERATINGCFM
  682.  
  683. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr)
  684. {
  685.     ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
  686. }
  687.  
  688. #else
  689.  
  690. static    pascal    TMTaskPtr    GetTMTaskPtr(void)
  691.     ONEWORDINLINE(0x2e89);    /* MOVE.L A1,(SP) */
  692.     
  693. static    void    TimeOutTask(void)
  694. {
  695.     ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
  696. }
  697.  
  698. #endif
  699.  
  700. #if    __WANTPASCALELIMINATION
  701. #define    pascal    
  702. #endif
  703.  
  704. /*****************************************************************************/
  705.  
  706. /*
  707. **    GetDirModDate returns the modification date of a directory. If there is
  708. **    an error getting the modification date, -1 is returned to indicate
  709. **    something went wrong.
  710. */
  711. static    long    GetDirModDate(short vRefNum,
  712.                               long dirID)
  713. {
  714.     CInfoPBRec pb;
  715.     Str31 tempName;
  716.     long modDate;
  717.  
  718.     /* Protection against File Sharing problem */
  719.     tempName[0] = 0;
  720.     pb.dirInfo.ioNamePtr = tempName;
  721.     pb.dirInfo.ioVRefNum = vRefNum;
  722.     pb.dirInfo.ioDrDirID = dirID;
  723.     pb.dirInfo.ioFDirIndex = -1;    /* use ioDrDirID */
  724.     
  725.     if ( PBGetCatInfoSync(&pb) == noErr )
  726.     {
  727.         modDate = pb.dirInfo.ioDrMdDat;
  728.     }
  729.     else
  730.     {
  731.         modDate = -1;
  732.     }
  733.     
  734.     return ( modDate );
  735. }
  736.  
  737. /*****************************************************************************/
  738.  
  739. pascal    OSErr    IndexedSearch(CSParamPtr pb,
  740.                               long dirID)
  741. {
  742.     static LevelRecHandle    searchStack = NULL;        /* static handle to LevelRec stack */
  743.     static Size                searchStackSize = 0;    /* size of static handle */
  744.     SearchPositionRecPtr    catPosition;
  745.     long                    modDate;
  746.     short                    index;
  747.     ExtendedTMTask            timerTask;
  748.     OSErr                    result;
  749.     short                    realVRefNum;
  750.     Str63                    itemName;
  751.     CInfoPBRec                cPB;
  752.     long                    tempLong;
  753.     Boolean                    includeFiles;
  754.     Boolean                    includeDirs;
  755.     Boolean                    includeNames;
  756.     Str63                    upperName;
  757.     
  758.     timerTask.stopSearch = false;    /* don't stop yet! */
  759.     
  760.     /* If request has a timeout, install a Time Manager task. */
  761.     if ( pb->ioSearchTime != 0 )
  762.     {
  763.         /* Start timer */
  764.         timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
  765.         InsTime((QElemPtr)&(timerTask.theTask));
  766.         PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
  767.     }
  768.     
  769.     /* Check the parameter block passed for things that we don't want to assume */
  770.     /* are OK later in the code. For example, make sure pointers to data structures */
  771.     /* and buffers are not NULL.  And while we're in there, see if the request */
  772.     /* specified searching for files, directories, or both, and see if the search */
  773.     /* was by full or partial name. */
  774.     result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
  775.     if ( result == noErr )
  776.     {
  777.         pb->ioActMatchCount = 0;    /* no matches yet */
  778.     
  779.         if ( includeNames )
  780.         {
  781.             /* The search includes seach by full or partial name. */
  782.             /* Make an upper case copy of the match string to pass to */
  783.             /* CheckForMatches. */
  784.             BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
  785.                             upperName,
  786.                             pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
  787.             /* Use the same non-international call the File Manager uses */
  788.             UpperString(upperName, true);
  789.         }
  790.         
  791.         /* Prevent casting to my type throughout code */
  792.         catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
  793.         
  794.         /* Create searchStack first time called */
  795.         if ( searchStack == NULL )
  796.         {
  797.             searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
  798.         }
  799.         
  800.         /* Make sure searchStack really exists */
  801.         if ( searchStack != NULL )
  802.         {
  803.             searchStackSize = InlineGetHandleSize((Handle)searchStack);
  804.             
  805.             /* See if the search is a new search or a resumed search. */
  806.             if ( catPosition->initialize == 0 )
  807.             {
  808.                 /* New search. */
  809.                 
  810.                 /* Get the real vRefNum and fill in catPosition->initialize. */ 
  811.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
  812.                 if ( result == noErr )
  813.                 {
  814.                     /* clear searchStack */
  815.                     catPosition->stackDepth = 0;
  816.                     
  817.                     /* use dirID parameter passed and... */
  818.                     index = -1;    /* start with the passed directory itself! */
  819.                 }
  820.             }
  821.             else
  822.             {
  823.                 /* We're resuming a search. */
  824.     
  825.                 /* Get the real vRefNum and make sure catPosition->initialize is valid. */ 
  826.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
  827.                 if ( result == noErr )
  828.                 {
  829.                     /* Make sure the resumed search is to the same volume! */
  830.                     if ( catPosition->initialize == tempLong )
  831.                     {
  832.                         /* For resume, catPosition->stackDepth > 0 */
  833.                         if ( catPosition->stackDepth > 0 )
  834.                         {
  835.                             /* Position catPosition->stackDepth to access last saved level */
  836.                             --(catPosition->stackDepth);
  837.             
  838.                             /* Get the dirID and index for the next item */
  839.                             dirID = (*searchStack)[catPosition->stackDepth].dirID;
  840.                             index = (*searchStack)[catPosition->stackDepth].index;
  841.                             
  842.                             /* Check the dir's mod date against the saved mode date on our "stack" */
  843.                             modDate = GetDirModDate(realVRefNum, dirID);
  844.                             if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  845.                             {
  846.                                 result = catChangedErr;
  847.                             }
  848.                         }
  849.                         else
  850.                         {
  851.                             /* Invalid catPosition record was passed */
  852.                             result = paramErr;
  853.                         }
  854.                     }
  855.                     else
  856.                     {
  857.                         /* The volume is not the same */
  858.                         result = catChangedErr;
  859.                     }
  860.                 }
  861.             }
  862.             
  863.             if ( result == noErr )
  864.             {
  865.                 /* ioNamePtr and ioVRefNum only need to be set up once. */
  866.                 cPB.hFileInfo.ioNamePtr = itemName;
  867.                 cPB.hFileInfo.ioVRefNum = realVRefNum;
  868.                 
  869.                 /*
  870.                 **    Here's the loop that:
  871.                 **        Finds the next item on the volume.
  872.                 **        If noErr, calls the code to check for matches and add matches
  873.                 **            to the match buffer.
  874.                 **        Sets up dirID and index for to find the next item on the volume.
  875.                 **
  876.                 **    The looping ends when:
  877.                 **        (a) an unexpected error is returned by PBGetCatInfo. All that
  878.                 **            is expected is noErr and fnfErr (after the last item in a
  879.                 **            directory is found).
  880.                 **        (b) the caller specified a timeout and our Time Manager task
  881.                 **            has fired.
  882.                 **        (c) the number of matches requested by the caller has been found.
  883.                 **        (d) the last item on the volume was found.
  884.                 */
  885.                 do
  886.                 {
  887.                     /* get the next item */
  888.                     cPB.hFileInfo.ioFDirIndex = index;
  889.                     cPB.hFileInfo.ioDirID = dirID;
  890.                     result = PBGetCatInfoSync(&cPB);
  891.                     if ( index != -1 )
  892.                     {
  893.                         if ( result == noErr )
  894.                         {
  895.                             /* We found something */
  896.         
  897.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  898.                             
  899.                             ++index;
  900.                             if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  901.                             {
  902.                                 /* It's a directory */
  903.                                 
  904.                                 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  905.                                 if ( result == noErr )
  906.                                 {
  907.                                     /* Save the current state on the searchStack */
  908.                                     /* when we come back, this is where we'll start */
  909.                                     (*searchStack)[catPosition->stackDepth].dirID = dirID;
  910.                                     (*searchStack)[catPosition->stackDepth].index = index;
  911.                                     (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  912.                                     
  913.                                     /* position catPosition->stackDepth for next saved level */
  914.                                     ++(catPosition->stackDepth);
  915.                                     
  916.                                     /* The next item to get is the 1st item in the child directory */
  917.                                     dirID = cPB.dirInfo.ioDrDirID;
  918.                                     index = 1;
  919.                                 }
  920.                             }
  921.                             /* else do nothing for files */
  922.                         }
  923.                         else
  924.                         {
  925.                             /* End of directory found (or we had some error and that */
  926.                             /* means we have to drop out of this directory). */
  927.                             /* Restore last thing put on stack and */
  928.                             /* see if we need to continue or quit. */
  929.                             if ( catPosition->stackDepth > 0 )
  930.                             {
  931.                                 /* position catPosition->stackDepth to access last saved level */
  932.                                 --(catPosition->stackDepth);
  933.                                 
  934.                                 dirID = (*searchStack)[catPosition->stackDepth].dirID;
  935.                                 index = (*searchStack)[catPosition->stackDepth].index;
  936.                                 
  937.                                 /* Check the dir's mod date against the saved mode date on our "stack" */
  938.                                 modDate = GetDirModDate(realVRefNum, dirID);
  939.                                 if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  940.                                 {
  941.                                     result = catChangedErr;
  942.                                 }
  943.                                 else
  944.                                 {
  945.                                     /* Going back to ancestor directory. */
  946.                                     /* Clear error so we can continue. */
  947.                                     result = noErr;
  948.                                 }
  949.                             }
  950.                             else
  951.                             {
  952.                                 /* We hit the bottom of the stack, so we'll let the */
  953.                                 /* the eofErr drop us out of the loop. */
  954.                                 result = eofErr;
  955.                             }
  956.                         }
  957.                     }
  958.                     else
  959.                     {
  960.                         /* Special case for index == -1; that means that we're starting */
  961.                         /* a new search and so the first item to check is the directory */
  962.                         /* passed to us. */
  963.                         if ( result == noErr )
  964.                         {
  965.                             /* We found something */
  966.         
  967.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  968.                             
  969.                             /* Now, set the index to 1 and then we're ready to look inside */
  970.                             /* the passed directory. */
  971.                             index = 1;
  972.                         }
  973.                     }
  974.                 } while ( (!timerTask.stopSearch) &&    /* timer hasn't fired */
  975.                           (result == noErr) &&            /* no unexpected errors */
  976.                           (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
  977.                 
  978.                 /* Did we drop out of the loop because of timeout or */
  979.                 /* ioReqMatchCount was found? */
  980.                 if ( result == noErr )
  981.                 {
  982.                     result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  983.                     if ( result == noErr )
  984.                     {
  985.                         /* Either there was a timeout or ioReqMatchCount was reached. */
  986.                         /* Save the dirID and index for the next time we're called. */
  987.                         
  988.                         (*searchStack)[catPosition->stackDepth].dirID = dirID;
  989.                         (*searchStack)[catPosition->stackDepth].index = index;
  990.                         (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  991.                         
  992.                         /* position catPosition->stackDepth for next saved level */
  993.                         
  994.                         ++(catPosition->stackDepth);
  995.                     }
  996.                 }
  997.             }
  998.         }
  999.         else
  1000.         {
  1001.             /* searchStack Handle could not be allocated */
  1002.             result = memFullErr;
  1003.         }
  1004.     }
  1005.     
  1006.     if ( pb->ioSearchTime != 0 )
  1007.     {
  1008.         /* Stop Time Manager task here if it was installed */
  1009.         RmvTime((QElemPtr)&(timerTask.theTask));
  1010.         DisposeRoutineDescriptor(timerTask.theTask.tmAddr);
  1011.     }
  1012.     
  1013.     return ( result );
  1014. }
  1015.  
  1016. /*****************************************************************************/
  1017.  
  1018. pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
  1019. {
  1020.     static Boolean            fullExtFSDispatchingtested = false;
  1021.     static Boolean            hasFullExtFSDispatching = false;
  1022.     OSErr                     result;
  1023.     Boolean                    supportsCatSearch;
  1024.     long                    response;
  1025.     GetVolParmsInfoBuffer    volParmsInfo;
  1026.     long                    infoSize;
  1027.     
  1028.     result = noErr;
  1029.  
  1030.     /* See if File Manager will pass CatSearch requests to external file systems */
  1031.     /* we'll store the results in a static variable so we don't have to call Gestalt */
  1032.     /* everytime we're called. */
  1033.     if ( !fullExtFSDispatchingtested )
  1034.     {
  1035.         fullExtFSDispatchingtested = true;
  1036.         if ( Gestalt(gestaltFSAttr, &response) == noErr )
  1037.         {
  1038.             hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
  1039.         }
  1040.     }
  1041.     
  1042.     /* CatSearch is a per volume attribute, so we have to check each time we're */
  1043.     /* called to see if it is available on the volume specified. */
  1044.     supportsCatSearch = false;
  1045.     if ( hasFullExtFSDispatching )
  1046.     {
  1047.         infoSize = sizeof(GetVolParmsInfoBuffer);
  1048.         result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
  1049.                             &volParmsInfo, &infoSize);
  1050.         if ( result == noErr )
  1051.         {
  1052.             supportsCatSearch = hasCatSearch(volParmsInfo);
  1053.         }
  1054.     }
  1055.     
  1056.     /* noErr or paramErr is OK here. */
  1057.     /* paramErr just means that GetVolParms isn't supported by this volume */
  1058.     if ( (result == noErr) || (result == paramErr) )
  1059.     {
  1060.         if ( supportsCatSearch )
  1061.         {
  1062.             /* Volume supports CatSearch so use it. */
  1063.             /* CatSearch is faster than an indexed search. */
  1064.             result = PBCatSearchSync(paramBlock);
  1065.         }
  1066.         else
  1067.         {
  1068.             /* Volume doesn't support CatSearch so */
  1069.             /* search using IndexedSearch from root directory. */
  1070.             result = IndexedSearch(paramBlock, fsRtDirID);
  1071.         }
  1072.     }
  1073.     
  1074.     return ( result );
  1075. }
  1076.  
  1077. /*****************************************************************************/
  1078.  
  1079. pascal    OSErr    NameFileSearch(ConstStr255Param volName,
  1080.                                short vRefNum,
  1081.                                ConstStr255Param fileName,
  1082.                                FSSpecPtr matches,
  1083.                                long reqMatchCount,
  1084.                                long *actMatchCount,
  1085.                                Boolean newSearch,
  1086.                                Boolean partial)
  1087. {
  1088.     CInfoPBRec        searchInfo1, searchInfo2;
  1089.     HParamBlockRec    pb;
  1090.     OSErr            error;
  1091.     static CatPositionRec catPosition;
  1092.     static short    lastVRefNum = 0;
  1093.     
  1094.     /* get the real volume reference number */
  1095.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1096.     if ( error != noErr )
  1097.         return ( error );
  1098.     
  1099.     pb.csParam.ioNamePtr = NULL;
  1100.     pb.csParam.ioVRefNum = vRefNum;
  1101.     pb.csParam.ioMatchPtr = matches;
  1102.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1103.     if ( partial )    /* tell CatSearch what we're looking for: */
  1104.     {
  1105.         pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib;    /* partial name file matches or */
  1106.     }
  1107.     else
  1108.     {
  1109.         pb.csParam.ioSearchBits =  fsSBFullName + fsSBFlAttrib;        /* full name file matches */
  1110.     }
  1111.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1112.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1113.     pb.csParam.ioSearchTime = 0;
  1114.     if ( (newSearch) ||                /* If caller specified new search */
  1115.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1116.     {
  1117.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1118.     }
  1119.     pb.csParam.ioCatPosition = catPosition;
  1120.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1121.  
  1122.     /* search for fileName */
  1123.     searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
  1124.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1125.     
  1126.     /* only match files (not directories) */
  1127.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1128.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1129.  
  1130.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1131.     
  1132.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1133.          (error == eofErr) )                            /* found, then the call was successful so */
  1134.     {
  1135.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1136.     }
  1137.     else
  1138.     {
  1139.         *actMatchCount = 0;                            /* else no matches found */
  1140.     }
  1141.     
  1142.     if ( (error == noErr) ||                        /* If no errors */
  1143.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1144.     {
  1145.         catPosition = pb.csParam.ioCatPosition;
  1146.         lastVRefNum = vRefNum;
  1147.             /* we can probably start the next search where we stopped this time */
  1148.     }
  1149.     else
  1150.     {
  1151.         catPosition.initialize = 0;
  1152.             /* start the next search from beginning of catalog */
  1153.     }
  1154.     
  1155.     if ( pb.csParam.ioOptBuffer != NULL )
  1156.     {
  1157.         DisposePtr(pb.csParam.ioOptBuffer);
  1158.     }
  1159.         
  1160.     return ( error );
  1161. }
  1162.  
  1163. /*****************************************************************************/
  1164.  
  1165. pascal    OSErr    CreatorTypeFileSearch(ConstStr255Param volName,
  1166.                                       short vRefNum,
  1167.                                       OSType creator,
  1168.                                       OSType fileType,
  1169.                                       FSSpecPtr matches,
  1170.                                       long reqMatchCount,
  1171.                                       long *actMatchCount,
  1172.                                       Boolean newSearch)
  1173. {
  1174.     CInfoPBRec        searchInfo1, searchInfo2;
  1175.     HParamBlockRec    pb;
  1176.     OSErr            error;
  1177.     static CatPositionRec catPosition;
  1178.     static short    lastVRefNum = 0;
  1179.     
  1180.     /* get the real volume reference number */
  1181.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1182.     if ( error != noErr )
  1183.         return ( error );
  1184.     
  1185.     pb.csParam.ioNamePtr = NULL;
  1186.     pb.csParam.ioVRefNum = vRefNum;
  1187.     pb.csParam.ioMatchPtr = matches;
  1188.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1189.     pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;    /* Looking for finder info file matches */
  1190.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1191.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1192.     pb.csParam.ioSearchTime = 0;
  1193.     if ( (newSearch) ||                /* If caller specified new search */
  1194.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1195.     {
  1196.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1197.     }
  1198.     pb.csParam.ioCatPosition = catPosition;
  1199.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1200.  
  1201.     /* no fileName */
  1202.     searchInfo1.hFileInfo.ioNamePtr = NULL;
  1203.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1204.     
  1205.     /* only match files (not directories) */
  1206.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1207.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1208.     
  1209.     /* search for creator; if creator = 0x00000000, ignore creator */
  1210.     searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  1211.     if ( creator == (OSType)0x00000000 )
  1212.     {
  1213.         searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000;
  1214.     }
  1215.     else
  1216.     {
  1217.         searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff;
  1218.     }
  1219.     
  1220.     /* search for fileType; if fileType = 0x00000000, ignore fileType */
  1221.     searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
  1222.     if ( fileType == (OSType)0x00000000 )
  1223.     {
  1224.         searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000;
  1225.     }
  1226.     else
  1227.     {
  1228.         searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff;
  1229.     }
  1230.     
  1231.     /* zero all other FInfo fields */
  1232.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1233.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1234.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1235.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1236.     
  1237.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1238.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1239.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1240.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1241.  
  1242.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1243.     
  1244.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1245.          (error == eofErr) )                            /* found, then the call was successful so */
  1246.     {
  1247.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1248.     }
  1249.     else
  1250.     {
  1251.         *actMatchCount = 0;                            /* else no matches found */
  1252.     }
  1253.     
  1254.     if ( (error == noErr) ||                        /* If no errors */
  1255.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1256.     {
  1257.         catPosition = pb.csParam.ioCatPosition;
  1258.         lastVRefNum = vRefNum;
  1259.             /* we can probably start the next search where we stopped this time */
  1260.     }
  1261.     else
  1262.     {
  1263.         catPosition.initialize = 0;
  1264.             /* start the next search from beginning of catalog */
  1265.     }
  1266.     
  1267.     if ( pb.csParam.ioOptBuffer != NULL )
  1268.     {
  1269.         DisposePtr(pb.csParam.ioOptBuffer);
  1270.     }
  1271.         
  1272.     return ( error );
  1273. }
  1274.  
  1275. /*****************************************************************************/
  1276.